# encoding=utf-8
import webbrowser
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
from data import Global


def markdown_render(text_widget, lines):
    """ 伪Markdown渲染器 """

    def color():
        if line.startswith('<RED> '):
            return line[6:], 'Red'
        elif line.startswith('<WHITE> '):
            return line[8:], 'Snow'
        elif line.startswith('<GREEN> '):
            return line[8:], 'Green'
        elif line.startswith('<BLUE> '):
            return line[7:], 'Blue'
        elif line.startswith('<GOLDEN> '):
            return line[9:], 'Gold'
        elif line.startswith('<GREY> '):
            return line[7:], 'Grey'
        elif line.startswith('<PURPLE> '):
            return line[9:], 'Purple'
        elif line.startswith('<ORANGE> '):
            return line[9:], 'Orange'
        elif line.startswith('<CYAN> '):
            return line[7:], 'DarkTurquoise'
        return line, 'Black'

    def title():
        if line.startswith('# '):
            return line[2:], 16, 'bold'
        elif line.startswith('## '):
            return line[3:], 15, 'bold'
        elif line.startswith('### '):
            return line[4:], 14, 'bold'
        elif line.startswith('#### '):
            return line[5:], 13, 'bold'
        elif line.startswith('##### '):
            return line[6:], 12, 'bold'
        elif line.startswith('###### '):
            return line[7:], 11, 'bold'
        elif line.startswith('####### '):
            return line[8:], 9, 'bold'
        elif line.startswith('######## '):
            return line[9:], 8, 'bold'
        return line, 10, ''

    def style():
        if line.startswith('***') and line.endswith('***'):
            return line[3:-3], 'bold', 'italic'
        elif line.startswith('**') and line.endswith('**'):
            return line[2:-2], 'bold', ''
        elif line.startswith('*') and line.endswith('*'):
            return line[1:-1], f_bold, 'italic'
        return line, f_bold, ''

    def types():
        ckb_style = {
            'master': text_widget,
            'font': ('黑体', f_size, ' '.join([f_bold, f_italic])),
            'fg': f_color,
            'bg': 'Snow'
        }
        if line.startswith(('- [ ] ', ': - [ ] ')):
            index = 6
            if line.startswith(':'):
                index = 8
                text_widget.insert('end', '\t')
            ckb = tk.Checkbutton(text=line[index:], **ckb_style)
            ckb.deselect()
            text_widget.window_create('end', window=ckb)
            text_widget.insert('end', '\n')
            return None, None, None
        elif line.startswith(('- [x] ', ': - [x] ')):
            index = 6
            if line.startswith(':'):
                index = 8
                text_widget.insert('end', '\t')
            ckb = tk.Checkbutton(text=line[index:], **ckb_style)
            ckb.select()
            text_widget.window_create('end', window=ckb)
            text_widget.insert('end', '\n')
            return None, None, None
        elif line.startswith('> - '):
            return '▌ • %s' % line[4:], 'Gray' if f_color == 'Black' else f_color, f_size
        elif line.startswith('> : '):
            return '▌ \t%s' % line[4:], 'Gray' if f_color == 'Black' else f_color, f_size
        elif line.startswith('> '):
            return '▌  %s' % line[2:], 'Gray' if f_color == 'Black' else f_color, f_size
        elif line.startswith(': - '):
            return '\t• %s' % line[4:], f_color, f_size
        elif line.startswith(': '):
            return '\t%s' % line[2:], f_color, f_size
        elif line.startswith('- ') or line.startswith('* ') or line.startswith('+ '):
            return '• ' + line[2:], f_color, max(11, f_size)
        return line, f_color, f_size

    def get_url():
        if 'http://' in line or 'https://' in line:
            return 'http%s' % line.split('http')[1].split()[0]
        return None

    try:
        count = text_widget.get('1.0', 'end').count('\n')
        for line in lines.split('\n'):
            line, f_color = color()           # 字体颜色
            line, f_size, f_bold = title()    # 字体大小
            line, f_bold, f_italic = style()  # 字体样式
            line, f_color, f_size = types()   # 其他类型
            if line is None:
                continue
            font = ('黑体', f_size, ' '.join([f_bold, f_italic]).strip())
            text_widget.insert('end', '%s\n' % line)
            text_widget.tag_add("tag-%s" % count, "%s.0" % count, "%s.end" % count)
            url = get_url()
            if url:
                f_color = 'Blue'
                text_widget.tag_bind('tag-%s' % count, '<Enter>', lambda event: text_widget.config(cursor='hand2'))
                text_widget.tag_bind('tag-%s' % count, '<Leave>', lambda event: text_widget.config(cursor='xterm'))
                text_widget.tag_bind('tag-%s' % count, '<Button-1>', lambda event, x=url: webbrowser.open(x))
            text_widget.tag_config("tag-%s" % count, font=font, foreground=f_color)
            count += 1
    finally:
        pass


class WidgetTip:
    _toplevel = None

    @classmethod
    def info(cls, info):

        messagebox.showinfo('Information', info)

    @classmethod
    def error(cls, info):
        messagebox.showerror('Error', info)

    @classmethod
    def ask(cls, info):
        return messagebox.askokcancel('Confirm', info)

    @classmethod
    def warn(cls, info):
        messagebox.showwarning('Warning', info)

    @classmethod
    def enter_tips(cls, widget, text, width=20, height=20):
        def enter(event):
            cls._enter_tip(widget, text, width, height)

        def leave(event):
            cls._hide_tip()

        widget.bind('<Enter>', enter)
        widget.bind('<Leave>', leave)

    @classmethod
    def _enter_tip(cls, widget, text, width, height):
        if cls._toplevel:
            return
        x, y, cx, cy = widget.bbox("insert")
        x = x + widget.winfo_rootx() + width
        y = y + widget.winfo_rooty() - height
        cls._toplevel = tw = tk.Toplevel(widget)
        tw.overrideredirect(True)
        tw.wm_attributes('-topmost', 1)
        tw.geometry("+%d+%d" % (x, y))
        tk.Label(tw, text=text, font=(Global.app_font, "8"), justify='left', background="#ffffe0",
                 relief='solid', borderwidth=1).pack(ipadx=1)

    @classmethod
    def _hide_tip(cls):
        if cls._toplevel:
            cls._toplevel.destroy()
            cls._toplevel = None


class TopWinBase(object):
    master = None
    _instance = None

    def __new__(cls, *args, **kw):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
        return cls._instance

    def show(self, *args, **kw):
        if self.master:
            self.master.wm_attributes('-topmost', 1)
            return
        self.master = tk.Toplevel()
        self.master.protocol("WM_DELETE_WINDOW", self.close)
        self.pack(*args, **kw)

    def pack(self, *args, **kw):
        pass

    def resize(self, w, h):
        self.master.geometry('{}x{}'.format(w, h))
        self.master.resizable(False, False)

    def set_position(self, x, y):
        self.master.geometry('+{}+{}'.format(x, y))

    def topping(self, top=True):
        if not self.master:
            return
        n = 1 if top else 0
        self.master.wm_attributes('-topmost', n)

    def close(self):
        if not self.master:
            return
        self.master.destroy()
        self.master = None


class TitleFrame(object):

    def __init__(self, master, width, height, text=None, outer=False, side='top', **kwargs):
        if text or outer:
            _master = tk.LabelFrame(master, text=text, width=width, height=height, **kwargs)
        else:
            _master = tk.Frame(master, width=width, height=height, **kwargs)
        _master.pack(anchor='w', side=side, fill='both')
        # _master.pack_propagate(False)
        self._master = _master
        self.body = tk.Frame(_master, height=height)
        self.body.pack(side='top', fill='both')

    def master(self):
        return self.body

    def destroy(self):
        self.body.destroy()
        self._master.destroy()

    def configure(self, *args):
        self.body.configure(*args)


class LabelCombobox(object):
    def __init__(self, master, text, values, default='', row=0, column=0, lab_width=16, com_width=30):
        tk.Label(master, text=text, width=lab_width, anchor='e').grid(row=row, column=column, padx=20)
        self.Combobox = ttk.Combobox(master, values=values, width=com_width, state='readonly')
        self.Combobox.grid(row=row, column=column + 1, padx=10, pady=10)
        index = values.index(default) if (default and default in values) else 0
        self.Combobox.current(index)

    def bind(self, callback):
        self.Combobox.bind("<<ComboboxSelected>>", callback)

    def current(self, index):
        self.Combobox.current(index)

    def get(self):
        return self.Combobox.get()


class BaseButton(tk.Label):

    def __init__(self, master, text=None, image=None, command=None, *args, **kwargs):
        tk.Label.__init__(self, master, text=text, image=image, *args, **kwargs)

        self.command = command

        def enter(event=None):
            self['bg'] = 'DodgerBlue3'

        def leave(event=None):
            self['bg'] = Global.current_theme[1]

        def down(event=None):
            self['bg'] = 'Grey80'

        def up(event=None):
            enter()

        def call(event=None):
            down()
            if self.command:
                self.command()

        self.bind("<Enter>", enter)
        self.bind("<Leave>", leave)
        self.bind("<ButtonRelease-1>", up)
        self.bind("<Button-1>", call)

    def update_command(self, command):
        self.command = command


class MultiSelector(object):

    def __init__(self, master, values=None, width=70, size=10, cut=' '):
        self.master = master
        self.values = values
        self.width = width
        self.size = size
        self.cut = cut
        self.choose_var = [tk.IntVar() for _ in values]
        self.toplevel = None
        self.entry = ttk.Entry(master, font=('Mono', size), width=width)
        self.entry.bind("<Button-1>", self.show_selector)
        self.is_disabled = False

    def grid(self, **args):
        self.entry.grid(args)
        self.entry.configure({'state': 'readonly'})

    def show_selector(self, event=None):
        if self.toplevel or self.is_disabled:
            return
        x = self.entry.winfo_rootx()
        y = self.entry.winfo_rooty()  # + self.entry.winfo_height()
        self.toplevel = tw = tk.Toplevel(self.entry)
        tw.overrideredirect(True)
        tw.wm_attributes('-topmost', 1)
        tw.geometry("+%d+%d" % (x, y))
        text = scrolledtext.ScrolledText(tw, relief='ridge', width=self.width - 2, height=10)
        count = len(self.values)
        for i, value in enumerate(self.values):
            ckb = tk.Checkbutton(text, text=value, variable=self.choose_var[i], anchor='w',
                                 font=('Mono', self.size), bg='Snow', command=self.set_entry)
            text.window_create('end', window=ckb)
            if i == 0:
                text.insert('end', '    (空白处转动鼠标滚轮可下拉)\n')
                text.tag_add('tag-1', '1.1', 'end')
                text.tag_config("tag-1", foreground='Gray70')
            elif i != count - 1:
                text.insert('end', '\n')
        text.pack(fill='both')
        text.configure({'state': 'disabled'})
        text.bind('<Leave>', self.close)

    def reset_entry(self, text):
        self.entry.configure({'state': 'normal'})
        self.entry.delete('0', 'end')
        self.entry.insert('end', text)
        self.entry.configure({'state': 'readonly'})

    def set_entry(self, *args):
        en_text = []
        for i, var in enumerate(self.choose_var):
            if var.get():
                en_text.append(self.values[i])
        self.reset_entry(self.cut.join(en_text))

    def close(self, *args):
        if self.toplevel:
            self.toplevel.destroy()
            self.toplevel = None

    def current(self, index_list):
        en_text = []
        for index in index_list:
            self.choose_var[index].set(1)
            en_text.append(self.values[index])
        self.reset_entry(self.cut.join(en_text))

    def disable(self, flag=True):
        self.is_disabled = flag
        if flag:
            self.reset_entry('')
            self.entry.configure({'state': 'disabled'})
        else:
            self.entry.configure({'state': 'readonly'})

    def set_values(self, values):
        self.values = values
        self.choose_var = [tk.IntVar() for _ in values]
        self.reset_entry('')

    def get(self):
        return self.entry.get()


class TextEditor(object):

    def __init__(self, master, width=60, height=16, size=9):
        self.master = master
        self.text = scrolledtext.ScrolledText(self.master, font=('Mono', size), relief='ridge',
                                              width=width, height=height)
        self.real_lines = None

    def grid(self, **kwargs):
        self.text.grid(kwargs)

    def pack(self, **kwargs):
        self.text.pack(kwargs)

    def get_text(self):
        if self.real_lines:
            return self.real_lines
        return self.text.get('1.0', 'end')

    def clear(self):
        self.text.delete('1.0', 'end')
        self.real_lines = None

    def insert_text(self, info):
        self.text.insert('end', '%s\n' % info)

    def insert_image(self, img):
        self.text.window_create('end', window=tk.Label(self.text, image=img, bg='Snow'))

    def show_markdown(self, lines, see_end=False):
        self.real_lines = lines
        markdown_render(self.text, lines)
        if see_end:
            self.text.see('end')

    def configure(self, *args):
        self.text.configure(*args)
